package com.jintian.smart.kernel.flowable.service;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.jintian.smart.kernel.common.context.ContextHolder;
import com.jintian.smart.kernel.common.util.JsonUtil;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.flowable.constant.ProcessConstant;
import com.jintian.smart.kernel.flowable.domain.dto.MetaInfoDto;
import com.jintian.smart.kernel.flowable.domain.dto.ModelDto;
import com.jintian.smart.kernel.flowable.domain.view.ActDeModelVo;
import com.jintian.smart.kernel.flowable.domain.view.ModelVo;
import com.jintian.smart.kernel.flowable.mapper.IModelMapper;
import com.jintian.smart.kernel.flowable.util.ModelUtil;
import com.jintian.smart.kernel.orm.impl.AbstractEntityServiceImpl;
import jakarta.annotation.Resource;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.engine.PageQuery;
import org.beetl.sql.core.page.PageResult;
import org.beetl.sql.core.query.LambdaQuery;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Service
public class ModelService extends AbstractEntityServiceImpl<IModelMapper, ActDeModelVo> {
    @Autowired
    private SQLManager sqlManager;

    @Resource
    protected RepositoryService repositoryService;


    public List<ActDeModelVo> getActDeModels() {
        return this.entityMapper.getActDeModels();
    }


    public ActDeModelVo getActDeModelByModelId(String modelId) {
        LambdaQuery<ActDeModelVo> query = sqlManager.lambdaQuery(ActDeModelVo.class);
        return query.andEq("id", modelId).single();
    }

    public PageResult<ActDeModelVo> list(ModelDto modelBo, PageQuery pageQuery) {
        ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
        // 构建查询条件
        if (StringUtil.isNotBlank(modelBo.getModelKey())) {
            modelQuery.modelKey(modelBo.getModelKey());
        }
        if (StringUtil.isNotBlank(modelBo.getModelName())) {
            modelQuery.modelNameLike("%" + modelBo.getModelName() + "%");
        }
        if (StringUtil.isNotBlank(modelBo.getCategory())) {
            modelQuery.modelCategory(modelBo.getCategory());
        }
        // 执行查询
        long pageTotal = modelQuery.count();
        if (pageTotal <= 0) {
            return pageQuery.of(null, 0l);
        }
        long offset = pageQuery.getPageSize() * (pageQuery.getPageNo() - 1);
        List<Model> modelList = modelQuery.listPage((int) offset, pageQuery.getPageSize());
        List<ModelVo> modelVoList = new ArrayList<>(modelList.size());
        modelList.forEach(model -> {
            ModelVo modelVo = new ModelVo();
            modelVo.setModelId(model.getId());
            modelVo.setModelName(model.getName());
            modelVo.setModelKey(model.getKey());
            modelVo.setCategory(model.getCategory());
            modelVo.setCreateTime(model.getCreateTime());
            modelVo.setVersion(model.getVersion());
            MetaInfoDto metaInfo = JsonUtil.readValue(model.getMetaInfo(), MetaInfoDto.class);
            if (metaInfo != null) {
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setFormType(metaInfo.getFormType());
                modelVo.setFormId(metaInfo.getFormId());
            }
            modelVoList.add(modelVo);
        });
        PageResult<ActDeModelVo> page = pageQuery.of(modelVoList, pageTotal);
        return page;


//        Page<ModelVo> page = new Page<>();
//        page.setRecords(modelVoList);
//        page.setTotal(pageTotal);
//        return TableDataInfo.build(page);
    }

    public List<ModelVo> list(ModelDto modelBo) {
        ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
        // 构建查询条件
        if (StringUtil.isNotBlank(modelBo.getModelKey())) {
            modelQuery.modelKey(modelBo.getModelKey());
        }
        if (StringUtil.isNotBlank(modelBo.getModelName())) {
            modelQuery.modelNameLike("%" + modelBo.getModelName() + "%");
        }
        if (StringUtil.isNotBlank(modelBo.getCategory())) {
            modelQuery.modelCategory(modelBo.getCategory());
        }
        List<Model> modelList = modelQuery.list();
        List<ModelVo> modelVoList = new ArrayList<>(modelList.size());
        modelList.forEach(model -> {
            ModelVo modelVo = new ModelVo();
            modelVo.setModelId(model.getId());
            modelVo.setModelName(model.getName());
            modelVo.setModelKey(model.getKey());
            modelVo.setCategory(model.getCategory());
            modelVo.setCreateTime(model.getCreateTime());
            modelVo.setVersion(model.getVersion());
            MetaInfoDto metaInfo = JsonUtil.readValue(model.getMetaInfo(), MetaInfoDto.class);
            if (metaInfo != null) {
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setFormType(metaInfo.getFormType());
                modelVo.setFormId(metaInfo.getFormId());
            }
            modelVoList.add(modelVo);
        });
        return modelVoList;
    }

    public PageResult<ModelVo> historyList(ModelDto modelBo, PageQuery pageQuery) {
        ModelQuery modelQuery = repositoryService.createModelQuery().modelKey(modelBo.getModelKey()).orderByModelVersion().desc();
        // 执行查询（不显示最新版，-1）
        long pageTotal = modelQuery.count() - 1;
        if (pageTotal <= 0) {
            return pageQuery.of(null, 0l);
        }
        // offset+1，去掉最新版
        long offset = 1 + pageQuery.getPageSize() * (pageQuery.getPageNo() - 1);
        List<Model> modelList = modelQuery.listPage((int) offset, pageQuery.getPageSize());
        List<ModelVo> modelVoList = new ArrayList<>(modelList.size());
        modelList.forEach(model -> {
            ModelVo modelVo = new ModelVo();
            modelVo.setModelId(model.getId());
            modelVo.setModelName(model.getName());
            modelVo.setModelKey(model.getKey());
            modelVo.setCategory(model.getCategory());
            modelVo.setCreateTime(model.getCreateTime());
            modelVo.setVersion(model.getVersion());
            MetaInfoDto metaInfo = JsonUtil.readValue(model.getMetaInfo(), MetaInfoDto.class);
            if (metaInfo != null) {
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setFormType(metaInfo.getFormType());
                modelVo.setFormId(metaInfo.getFormId());
            }
            modelVoList.add(modelVo);
        });

        PageResult<ModelVo> page = pageQuery.of(modelVoList, pageTotal);
        return page;
//        Page<ModelVo> page = new Page<>();
//        page.setRecords(modelVoList);
//        page.setTotal(pageTotal);
//        return TableDataInfo.build(page);
    }

    public ModelVo getModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        // 获取流程图
        String bpmnXml = queryBpmnXmlById(modelId);
        ModelVo modelVo = new ModelVo();
        modelVo.setModelId(model.getId());
        modelVo.setModelName(model.getName());
        modelVo.setModelKey(model.getKey());
        modelVo.setCategory(model.getCategory());
        modelVo.setCreateTime(model.getCreateTime());
        modelVo.setVersion(model.getVersion());
        modelVo.setBpmnXml(bpmnXml);
        MetaInfoDto metaInfo = JsonUtil.readValue(model.getMetaInfo(), MetaInfoDto.class);
        if (metaInfo != null) {
            modelVo.setDescription(metaInfo.getDescription());
            modelVo.setFormType(metaInfo.getFormType());
            modelVo.setFormId(metaInfo.getFormId());
//            if (FormType.PROCESS.getType().equals(metaInfo.getFormType())) {
//                WfFormVo wfFormVo = formService.queryById(metaInfo.getFormId());
//                modelVo.setContent(wfFormVo.getContent());
//            }
        }
        return modelVo;
    }

    public String queryBpmnXmlById(String modelId) {
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        return StrUtil.utf8Str(bpmnBytes);
    }

    public void insertModel(ModelDto modelBo) {
        Model model = repositoryService.newModel();
        model.setName(modelBo.getModelName());
        model.setKey(modelBo.getModelKey());
        model.setCategory(modelBo.getCategory());
        String metaInfo = buildMetaInfo(new MetaInfoDto(), modelBo.getDescription());
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateModel(ModelDto modelBo) {
        // 根据模型Key查询模型信息
        Model model = repositoryService.getModel(modelBo.getModelId());
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        model.setCategory(modelBo.getCategory());
        MetaInfoDto metaInfoDto = JsonUtil.readValue(model.getMetaInfo(), MetaInfoDto.class);
        String metaInfo = buildMetaInfo(metaInfoDto, modelBo.getDescription());
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveModel(ModelDto modelBo) {
        // 查询模型信息
        Model model = repositoryService.getModel(modelBo.getModelId());
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        BpmnModel bpmnModel = ModelUtil.getBpmnModel(modelBo.getBpmnXml());
        if (ObjectUtil.isEmpty(bpmnModel)) {
            throw new RuntimeException("获取模型设计失败！");
        }
        String processName = bpmnModel.getMainProcess().getName();
        // 获取开始节点
        StartEvent startEvent = ModelUtil.getStartEvent(bpmnModel);
        if (ObjectUtil.isNull(startEvent)) {
            throw new RuntimeException("开始节点不存在，请检查流程设计是否有误！");
        }
        // 获取开始节点配置的表单Key
        if (StrUtil.isBlank(startEvent.getFormKey())) {
            throw new RuntimeException("请配置流程表单");
        }
        Model newModel;
        if (Boolean.TRUE.equals(modelBo.getNewVersion())) {
            newModel = repositoryService.newModel();
            newModel.setName(processName);
            newModel.setKey(model.getKey());
            newModel.setCategory(model.getCategory());
            newModel.setMetaInfo(model.getMetaInfo());
            newModel.setVersion(model.getVersion() + 1);
        } else {
            newModel = model;
            // 设置流程名称
            newModel.setName(processName);
        }
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        byte[] bpmnXmlBytes = StringUtil.getBytes(modelBo.getBpmnXml(), StandardCharsets.UTF_8);
        repositoryService.addModelEditorSource(newModel.getId(), bpmnXmlBytes);
    }

    @Transactional(rollbackFor = Exception.class)
    public void latestModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        Integer latestVersion = repositoryService.createModelQuery().modelKey(model.getKey()).latestVersion().singleResult().getVersion();
        if (model.getVersion().equals(latestVersion)) {
            throw new RuntimeException("当前版本已是最新版！");
        }
        // 获取 BPMN XML
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        Model newModel = repositoryService.newModel();
        newModel.setName(model.getName());
        newModel.setKey(model.getKey());
        newModel.setCategory(model.getCategory());
        newModel.setMetaInfo(model.getMetaInfo());
        newModel.setVersion(latestVersion + 1);
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        repositoryService.addModelEditorSource(newModel.getId(), bpmnBytes);
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(Collection<String> ids) {
        ids.forEach(id -> {
            Model model = repositoryService.getModel(id);
            if (ObjectUtil.isNull(model)) {
                throw new RuntimeException("流程模型不存在！");
            }
            repositoryService.deleteModel(id);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean deployModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        // 获取流程图
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        if (ArrayUtil.isEmpty(bpmnBytes)) {
            throw new RuntimeException("请先设计流程图！");
        }
        String bpmnXml = StringUtil.toEncodedString(bpmnBytes, StandardCharsets.UTF_8);
        BpmnModel bpmnModel = ModelUtil.getBpmnModel(bpmnXml);
        String processName = model.getName() + ProcessConstant.SUFFIX;
        // 部署流程
        Deployment deployment = repositoryService.createDeployment().name(model.getName()).key(model.getKey()).category(model.getCategory()).addBytes(processName, bpmnBytes).deploy();
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        // 修改流程定义的分类，便于搜索流程
        repositoryService.setProcessDefinitionCategory(procDef.getId(), model.getCategory());
        // 保存部署表单
//        return deployFormService.saveInternalDeployForm(deployment.getId(), bpmnModel);
        return true;
    }

    /**
     * 构建模型扩展信息
     *
     * @return
     */
    private String buildMetaInfo(MetaInfoDto metaInfo, String description) {
        // 只有非空，才进行设置，避免更新时的覆盖
        if (StringUtil.isNotBlank(description)) {
            metaInfo.setDescription(description);
        }
        if (StringUtil.isNotBlank(metaInfo.getCreateUser())) {
            metaInfo.setCreateUser(ContextHolder.getUserName());
        }
        return JsonUtil.toJson(metaInfo);
    }
}
